// Listing 16.7 Friend Class Illustrated
#include <cstddef>
#include <iostream>
using namespace std;

/**Part**/
// Abstract base class of parts
class Part
{
    public:
        Part() : itsPartNumber(1) {}
        Part(int PartNumber) : itsPartNumber(PartNumber) {}
        virtual ~Part() {}
        int GetPartNumber() const
        {
            return itsPartNumber;
        }
        virtual void Display() const = 0;
    private:
        int itsPartNumber;    
};

// implelmentation of pure virtual function so that
// derived classes can chain up
void Part::Display() const
{
    cout << "\nPart Number: ";
    cout << itsPartNumber << endl;
}

/**Car Part**/
class CarPart : public Part
{
    public:
        CarPart() : itsModelYear(94) {}
        CarPart(int year, int partNumber);
        virtual void Display() const
        {
            Part::Display();
            cout << "Model Year: ";
            cout << itsModelYear << endl;
        }
    private:
        int itsModelYear;
};

CarPart::CarPart(int year, int partNumber)
: itsModelYear(year), Part(partNumber)
{}

/**AirPlane Part**/
class AirPlanePart : public Part
{
    public:
        AirPlanePart() :itsEngineNumber(1) {}
        AirPlanePart(int EngineNumber, int PartNumber);
        virtual void Display() const
        {
            Part::Display();
            cout << "Engine No.: ";
            cout << itsEngineNumber << endl;
        }
    private:
        int itsEngineNumber;
};

AirPlanePart::AirPlanePart(int EngineNumber, int PartNumber)
: itsEngineNumber(EngineNumber), Part(PartNumber) {}

/**Part Node**/
class PartNode
{
    public:
        friend class PartsList;
        PartNode (Part*);
        ~PartNode();
        void SetNext(PartNode *node)
        {
            itsNext = node;
        }

        PartNode *GetNext() const;
        Part *GetPart() const;
    private:
        Part *itsPart;
        PartNode *itsNext;
};

PartNode::PartNode(Part* pPart)
: itsPart(pPart), itsNext(0) {}

PartNode::~PartNode()
{
    delete itsPart;
    itsPart = 0;
    delete itsNext;
    itsNext = 0;
}

// Returns NULL if no next PartNode
PartNode *PartNode::GetNext() const
{
    return itsNext;
}

Part *PartNode::GetPart() const
{
    if(itsPart)
    {
        return itsPart;
    }
    else
    {
        return NULL;
    }
}

/**Part List**/
class PartsList
{
    public:
        PartsList();
        ~PartsList();
        // needs copy constructor and operator equlas:
        void Iterate(void (Part::*f)() const) const;
        Part* Find(int &postion, int PartNumber) const;
        Part* GetFirst() const;
        void Insert(Part*);
        Part* operator[](int) const;
        int GetCount() const { return itsCount; }
        static PartsList& GetGlobalPartsList()
        {
            return GlobalPartsList;
        }
    private:
        PartNode* pHead;
        int itsCount;
        static PartsList GlobalPartsList;    
};

PartsList PartsList::GlobalPartsList;

// Implementations for Lists...
PartsList::PartsList()
: pHead(0), itsCount(0) {}

PartsList::~PartsList()
{
    delete pHead;
}

Part* PartsList::GetFirst() const
{
    if(pHead)
    {
        return pHead->itsPart;
    }
    else
    {
        return NULL;    // error catch here
    }
}

Part* PartsList::operator[](int offSet) const
{
    PartNode* pNode = pHead;
    if(!pHead)
    {
        return NULL;  // error catch here
    }

    if(offSet > itsCount)
    {
        return NULL;    // error
    }

    for (int i = 0; i < offSet; i++)
    {
        pNode = pNode->itsNext;
    }

    return pNode->itsPart;
}

Part* PartsList::Find(int &position, int PartNumber) const
{
    PartNode *pNode = 0;
    for (pNode = pHead, position = 0; pNode != NULL;
        pNode = pNode->itsNext, position++)
    {
        if(pNode->itsPart->GetPartNumber() == PartNumber)
        {
            break;
        }
    }    

    if(pNode == NULL)
    {
        return NULL;
    }
    else
    {
        return pNode->itsPart;
    }
}

void PartsList::Iterate(void (Part::*func)()const) const
{
    if(!pHead)
    {
        return;
    }

    PartNode* pNode = pHead;
    do {
      (pNode->itsPart->*func)();
    }while (pNode = pNode->itsNext);
}

void PartsList::Insert(Part * pPart)
{
    PartNode* pNode = new PartNode(pPart);
    PartNode* pCurrent = pHead;
    PartNode* pNext = 0;

    int New = pPart->GetPartNumber();
    int Next = 0;
    itsCount++;

    if(!pHead)
    {
        pHead = pNode;
        return;
    }

    // if this one is smaller than head
    // this one is the new head
    if(pHead->itsPart->GetPartNumber() > New)
    {
        pNode->itsNext = pHead;
        pHead = pNode;
        return;
    }

    for(;;)
    {
        // if there is no next, append this new one
        if(!pCurrent->itsNext)
        {
            pCurrent->itsNext = pNode;
            return;
        }

        // if this goes after thsi one and before the next
        // then insert it here, otherwise get the next
        pNext = pCurrent->itsNext;
        Next = pNext->itsPart->GetPartNumber();
        if(Next > New)
        {
            pCurrent->itsNext = pNode;
            pNode->itsNext = pNext;
            return;
        }
        pCurrent = pNext;
    }
}

/**PartsCatalog*/
class PartsCatalog : private PartsList
{
    public:
        void Insert(Part*);
        int Exists(int PartNumber);
        Part* Get(int PartNumber);
        PartsCatalog operator+(const PartsCatalog&);
        void ShowAll() { Iterate(Part::Display); }
};

void PartsCatalog::Insert(Part *newPart)
{
    int partNumber = newPart->GetPartNumber();
    int offset;
    if(!Find(offset, partNumber))
    {
        PartsCatalog::Insert(newPart);
    }
    else
    {
        cout << partNumber << " was the ";
        switch(offset)
        {
            case 0: cout << "first "; break;
            case 1: cout << "second "; break;
            case 2: cout << "third "; break;
            default: cout << offset + 1 << "th ";
        }
        cout << "entry. Rejected!" << endl;
    }
}

int PartsCatalog::Exists(int PartNumber)
{
    int offset;
    Find(offset, PartNumber);
    return offset;
}

Part* PartsCatalog::Get(int PartNumber)
{
    int offset;
    return (Find(offset, PartNumber));
}

int main(void)
{
    PartsCatalog pc;
    Part* pPart = 0;
    int PartNumber;
    int value;
    int choice = 99;

    while (choice != 0)
    {
        cout << "(0) Quit (1) Car (2)Plane: ";
        cin >> choice;

        if(choice != 0)
        {
            cout << "New PartNumber?: ";
            cin >> PartNumber;

            if(choice == 1)
            {
                cout << "Model Year?: ";
                cin >> value;
                pPart = new CarPart(value, PartNumber);
            }
            else
            {
                cout << "Engine Number?: ";
                cin >> value;
                pPart = new AirPlanePart(value, PartNumber);
            }
            pc.Insert(pPart);
        }
    }
    pc.ShowAll();
}